//cImgBuf.cpp
#include "cImgBuf.h"

/* Default constructor.
 */
ImgBuf::ImgBuf()
{
	this->_Create(200, 200);
}

/* Constructor with size.
 * Inputs:
 * -Width, Height: Dimensions of buffer.
 */
ImgBuf::ImgBuf(u32 Width, u32 Height)
{
	this->_Create(Width, Height);
}

/* PROTECTED: Internal constructor routine.
 * Inputs:
 * -Width, Height: Dimensions of buffer.
 */
void ImgBuf::_Create(u32 Width, u32 Height)
{
	this->FGRed = 255;
	this->FGGreen = 255;
	this->FGBlue = 255;
	this->FGAlpha = 255;
	this->Width = Width;
	this->Height = Height;
	this->Pixels = (Pixel*)malloc(Width * Height * sizeof(Pixel));
}

/* Destructor.
 */
ImgBuf::~ImgBuf()
{
	if(this->Pixels) free(this->Pixels); this->Pixels = NULL;
}

/* Retrieves a pointer to the pixel data.
 * Returns: obvious.
 */
ImgBuf::Pixel* ImgBuf::GetPixels()
{
	return this->Pixels;
}

/* Retrieves the buffer width.
 * Returns: obvious.
 */
u32 ImgBuf::GetWidth()
{
	return this->Width;
}

/* Retrieves the buffer height.
 * Returns: obvious.
 */
u32 ImgBuf::GetHeight()
{
	return this->Height;
}

/* Sets the foreground colour.
 * Inputs:
 * -R, G, B: Colour (0-255).
 * Notes:
 * -This is mainly provided to make the API compatible with DrawBox.
 */
void ImgBuf::SetFGColour(u8 R, u8 G, u8 B)
{
	this->FGRed = R;
	this->FGGreen = G;
	this->FGBlue = B;
}

/* Plots a pixel in the foreground colour.
 * Inputs:
 * -X, Y: Coordinates of pixel.
 */
void ImgBuf::DrawPixel(u32 X, u32 Y)
{
	this->DrawPixel(X, Y, this->FGRed, this->FGGreen, this->FGBlue);
}

/* Sets the foreground colour and plots a pixel in it.
 * Inputs:
 * -R, G, B: New foreground colour.
 * -X, Y: Coordinates of pixel.
 * Notes:
 * -For efficiency, does not set the colour if it's the same as the current one.
 */
void ImgBuf::DrawPixel(u32 X, u32 Y, u8 R, u8 G, u8 B)
{
	//todo: would it be faster to combine them into a word, instead of assigning
	//to individual components?
	if(!this->Pixels) return;
	if((X >= this->Width) || (Y >= this->Height)) return;
	this->Pixels[(Y * this->Width) + X].Red = R;
	this->Pixels[(Y * this->Width) + X].Green = G;
	this->Pixels[(Y * this->Width) + X].Blue = B;
	this->Pixels[(Y * this->Width) + X].Alpha = this->FGAlpha;
}

/* Draws a rectangle in the foreground colour.
 * Inputs:
 * -X, Y: Coordinates of top left corner.
 * -Width, Height: Size of rectangle.
 * -Filled: true to fill, false to draw outline only.
 * Notes:
 * -If Width or Height are zero, the dimensions of the box minus the coordinate
 *  is used. So you can clear to a colour by passing zero for X, Y, Width and
 *  Height.
 */
void ImgBuf::DrawRectangle(u32 X, u32 Y, u32 Width, u32 Height, bool Filled)
{
	if(!this->Pixels) return;
	if(!Width) Width = this->Width - X;
	if(!Height) Height = this->Height - Y;
	
	//Draw filled rectangle
	if(Filled)
	{
		for(u32 x=X; x<X+Width; x++)
		{
			for(u32 y=Y; y<Y+Height; y++)
			{
				if((x >= this->Width) || (y >= this->Height)) continue;
				this->Pixels[(y * this->Width) + x].Red = this->FGRed;
				this->Pixels[(y * this->Width) + x].Green = this->FGGreen;
				this->Pixels[(y * this->Width) + x].Blue = this->FGBlue;
				this->Pixels[(y * this->Width) + x].Alpha = this->FGAlpha;
			}
		}
		return;
	}
	
	//Draw top and bottom of unfilled rectangle
	for(u32 x=X; Y < this->Height && x < X+Width; x++)
	{
		if(x >= this->Width) continue;
		this->Pixels[(Y * this->Width) + x].Red = this->FGRed;
		this->Pixels[(Y * this->Width) + x].Green = this->FGGreen;
		this->Pixels[(Y * this->Width) + x].Blue = this->FGBlue;
		this->Pixels[(Y * this->Width) + x].Alpha = this->FGAlpha;
		this->Pixels[((Y +(Height-1)) * this->Width) + x].Red = this->FGRed;
		this->Pixels[((Y +(Height-1)) * this->Width) + x].Green = this->FGGreen;
		this->Pixels[((Y +(Height-1)) * this->Width) + x].Blue = this->FGBlue;
		this->Pixels[((Y +(Height-1)) * this->Width) + x].Alpha = this->FGAlpha;
	}
	
	//Draw sides of unfilled rectangle
	for(u32 y=Y+1; y<Y+Height-1; y++)
	{
		if(X < this->Width)
		{
			this->Pixels[(y * this->Width) + X].Red = this->FGRed;
			this->Pixels[(y * this->Width) + X].Green = this->FGGreen;
			this->Pixels[(y * this->Width) + X].Blue = this->FGBlue;
			this->Pixels[(y * this->Width) + X].Alpha = this->FGAlpha;
		}
		if((X + (Width - 1)) < this->Width)
		{
			this->Pixels[(y * this->Width) + X + (Width - 1)].Red = this->FGRed;
			this->Pixels[(y * this->Width) + X + (Width - 1)].Green = this->FGGreen;
			this->Pixels[(y * this->Width) + X + (Width - 1)].Blue = this->FGBlue;
			this->Pixels[(y * this->Width) + X + (Width - 1)].Alpha = this->FGAlpha;
		}
	}
}

/* Blits (copies) part of another ImgBuf.
 * Inputs:
 * -Src: Buffer to copy. A buffer can be copied onto itself.
 * -SX, SY: Coordinates to copy from.
 * -DX, DY: Coordniates to copy to.
 * -W, H: Size of rectangle to copy.
 */
void ImgBuf::BlitFrom(ImgBuf *Src, u32 SX, u32 SY, u32 DX, u32 DY, u32 W, u32 H)
{
	if((DX >= this->Width) || (DY >= this->Height) || !this->Pixels || !Src
	|| !Src->Pixels) return;
	
	for(u32 y=DY; y<H && y < this->Height; y++)
	{
		memcpy(&this->Pixels[(y * this->Width) + DX],
			&Src->Pixels[(y * Src->Width) + SX],
			W * sizeof(Pixel));
	}
}

/* Resizes the buffer.
 * Inputs:
 * -Width, Height: New size.
 * Returns: true on success, false on failure.
 * Notes:
 * -The buffer contents will be lost.
 * -Any existing pointers to the pixel data will be invalidated.
 */
bool ImgBuf::Resize(u32 Width, u32 Height)
{
	Pixel *NewPixels;
	NewPixels = (Pixel*)realloc(this->Pixels, Width * Height * sizeof(Pixel));
	if(!NewPixels) return false;
	this->Pixels = NewPixels;
	this->Width = Width;
	this->Height = Height;
	return true;
}

/* Clears to the foreground colour.
 */
void ImgBuf::Clear()
{
	this->Clear(this->FGRed, this->FGGreen, this->FGBlue, this->FGAlpha);
}

/* Clears to the specified colour.
 * Inputs:
 * -R, G, B, A: Colour to clear to.
 */
void ImgBuf::Clear(u8 R, u8 G, u8 B, u8 A)
{
	Pixel *Px;
	if((R == G) && (R == B) && (R == A))
	{
		memset(this->Pixels, R, sizeof(Pixel) * this->Width * this->Height);
		return;
	}
	
	Px = this->Pixels;
	for(u32 y=0; y<this->Height; y++)
	{
		for(u32 x=0; x<this->Width; x++)
		{
			Px->Red = R;
			Px->Green = G;
			Px->Blue = B;
			Px->Alpha = A;
			Px++;
		}
	}
}
